package com.bluesky.system.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeNode;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.bluesky.common.enums.StatusEnum;
import com.bluesky.common.exception.CustomException;
import com.bluesky.system.common.dto.SysDeptAddDTO;
import com.bluesky.system.common.dto.SysDeptEditDTO;
import com.bluesky.system.common.dto.SysDeptQueryDTO;
import com.bluesky.system.entity.SysDept;
import com.bluesky.system.entity.SysRoleDept;
import com.bluesky.system.entity.SysUser;
import com.bluesky.system.mapper.SysDeptMapper;
import com.bluesky.system.service.ISysDeptService;
import com.bluesky.system.service.ISysRoleDeptService;
import com.bluesky.system.service.ISysUserService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

/**
 * <p>
 * 部门表 服务实现类
 * </p>
 *
 * @author Kevin
 * @since 2021-06-10
 */
@Service
public class SysDeptServiceImpl extends ServiceImpl<SysDeptMapper, SysDept> implements ISysDeptService {

    @Resource
    private ISysUserService sysUserService;

    @Resource
    private ISysRoleDeptService sysRoleDeptService;

    @Override
    public IPage<SysDept> page(Page reqPage, SysDeptQueryDTO req) {
        LambdaQueryWrapper<SysDept> queryWrapper = Wrappers.lambdaQuery();
        queryWrapper.eq(Objects.nonNull(req.getParentId()), SysDept::getParentId, req.getParentId());
        queryWrapper.like(StrUtil.isNotBlank(req.getDeptName()), SysDept::getDeptName, req.getDeptName());
        queryWrapper.like(StrUtil.isNotBlank(req.getDeptFullname()), SysDept::getDeptFullname, req.getDeptFullname());
        queryWrapper.orderByAsc(SysDept::getSort);
        IPage<SysDept> page = this.page(reqPage, queryWrapper);
        List<SysDept> sysDeptList = this.list();
        page.getRecords().forEach(item -> {
            item.setChildren(getChildrenList(sysDeptList, item));
            if (item.getParentId().intValue() == 0) {
                item.setParentId(null);
            }
        });
        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void add(SysDeptAddDTO req) {
        if (!this.checkUniqueDeptName(req.getDeptName(), null)) {
            throw new CustomException("部门名称已存在");
        }
        SysDept entity = BeanUtil.copyProperties(req, SysDept.class);
        if (Objects.isNull(req.getParentId())) {
            entity.setParentId(0L);
            entity.setAncestors(entity.getParentId().toString());
        } else {
            SysDept parent = this.getById(req.getParentId());
            entity.setAncestors(parent.getAncestors() + "," + entity.getParentId().toString());
        }
        this.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void edit(SysDeptEditDTO req) {
        if (!this.checkUniqueDeptName(req.getDeptName(), req.getId())) {
            throw new CustomException("部门名称已存在");
        }
        if (req.getStatus().equals(StatusEnum.NO.getCode()) &&
                this.list(Wrappers.lambdaQuery(SysDept.class).eq(SysDept::getStatus, StatusEnum.YES.getCode()).apply("find_in_set({0}, ancestors)", req.getId())).size() > 0) {
            throw new CustomException("该部门存在未停用的下级部门");
        }
        SysDept entity = BeanUtil.copyProperties(req, SysDept.class);
        if (Objects.isNull(req.getParentId())) {
            entity.setParentId(0L);
            entity.setAncestors(entity.getParentId().toString());
        } else {
            SysDept parent = this.getById(req.getParentId());
            entity.setAncestors(parent.getAncestors() + "," + entity.getParentId().toString());
        }
        SysDept sysDept = this.getById(req.getId());
        // 更新子节点的祖级列表
        List<SysDept> sysDeptList = this.list(Wrappers.lambdaQuery(SysDept.class).apply("find_in_set({0}, ancestors)", req.getId()));
        sysDeptList.forEach(item -> {
            item.setAncestors(item.getAncestors().replaceFirst(sysDept.getAncestors(), entity.getAncestors()));
        });
        this.updateById(entity);
        // 启用该部门，则启用该部门的所有上级部门
        List<SysDept> parentList = Arrays.asList(entity.getAncestors().split(",")).stream().map(item ->
                new SysDept().setId(Long.parseLong(item)).setStatus(StatusEnum.YES.getCode())
        ).collect(Collectors.toList());
        this.updateBatchById(parentList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void remove(String ids) {
        List<String> idList = Arrays.asList(ids.split(","));
        idList.forEach(item -> {
            SysDept sysDept = this.getById(item);
            SysDept sysDeptChild = this.getOne(Wrappers.lambdaQuery(SysDept.class).eq(SysDept::getParentId, item).last("LIMIT 1"));
            if (Objects.nonNull(sysDeptChild)) {
                throw new CustomException(String.format("部门【%s】存在下级部门，无法删除", sysDept.getDeptName()));
            }
            SysUser sysUser = sysUserService.getOne(Wrappers.lambdaQuery(SysUser.class).eq(SysUser::getDeptId, item).last("LIMIT 1"));
            if (Objects.nonNull(sysUser)) {
                throw new CustomException(String.format("部门【%s】存在用户，无法删除", sysDept.getDeptName()));
            }
            SysRoleDept sysRoleDept = sysRoleDeptService.getOne(Wrappers.lambdaQuery(SysRoleDept.class).eq(SysRoleDept::getDeptId, item).last("LIMIT 1"));
            if (Objects.nonNull(sysRoleDept)) {
                throw new CustomException(String.format("部门【%s】已被使用，无法删除", sysDept.getDeptName()));
            }
        });
        this.removeByIds(Arrays.asList(ids.split(",")));
    }

    @Override
    public SysDept view(String id) {
        return this.getById(id);
    }

    @Override
    public List<Tree<String>> listDeptTree() {
        List<SysDept> sysDeptList = this.list();
        List treeNodeList = sysDeptList.stream().map(item -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setId(item.getId().toString());
            treeNode.setName(item.getDeptName());
            treeNode.setParentId(item.getParentId().toString());
            treeNode.setWeight(item.getSort());
            return treeNode;
        }).collect(Collectors.toList());
        List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
        return treeList;
    }

    @Override
    public List<Tree<String>> listDeptTreeAndExcludeNode(String nodeId) {
        List<SysDept> sysDeptList = this.list();
        if (StrUtil.isNotBlank(nodeId)) {
            Iterator<SysDept> it = sysDeptList.iterator();
            while (it.hasNext()) {
                SysDept sysDept = it.next();
                if (sysDept.getId().toString().equals(nodeId) || Arrays.asList(sysDept.getAncestors().split(",")).contains(nodeId)) {
                    it.remove();
                }
            }
        }
        List treeNodeList = sysDeptList.stream().map(item -> {
            TreeNode treeNode = new TreeNode();
            treeNode.setId(item.getId().toString());
            treeNode.setName(item.getDeptName());
            treeNode.setParentId(item.getParentId().toString());
            treeNode.setWeight(item.getSort());
            return treeNode;
        }).collect(Collectors.toList());
        List<Tree<String>> treeList = TreeUtil.build(treeNodeList, "0");
        return treeList;
    }

    private List<SysDept> getChildrenList(List<SysDept> sysDeptList, SysDept root) {
        List<SysDept> childrenList = sysDeptList.stream().filter(item -> item.getParentId().equals(root.getId())).sorted(Comparator.comparing(SysDept::getSort)).collect(Collectors.toList());
        childrenList.forEach(item -> {
            item.setChildren(getChildrenList(sysDeptList, item));
        });
        return childrenList;
    }

    private Boolean checkUniqueDeptName(String value, Long id) {
        if (StrUtil.isBlank(value)) {
            return true;
        }
        id = Objects.isNull(id) ? -1L : id;
        SysDept entity = this.getOne(Wrappers.lambdaQuery(SysDept.class).eq(SysDept::getDeptName, value).last("limit 1"));
        return Objects.isNull(entity) || entity.getId().longValue() == id.longValue();
    }

}
